package com.documentCreator;

import java.awt.List;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.StringBufferInputStream;
import java.io.StringWriter;
import java.nio.ByteBuffer;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.zip.*;

import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.*;
import javax.xml.xpath.XPathExpression;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.xpath.*;
import org.w3c.dom.Element;

public class documentCreator {
	
	//에러코드 및 리턴 상태상수
	public final int STATE_OK 							= 0;
	public final int STATE_FAIL 						= 1;
	public final int STATE_ERROR 						= (2 << 16);
	public final int STATE_ERROR_INPUTFILE_NOT_EXIST 	= (2 << 16) | 1;
	public final int STATE_ERROR_XML_PARSING_FAIL 		= (2 << 16) | 2;
	
	public String m_strProjectDatabaseURL;
	public String m_strProjectId;
	
	public class OOXMLNamespaceContext implements NamespaceContext {

	    @Override
	    public String getNamespaceURI(String prefix) {
	        if (prefix == null) 
	        	throw new NullPointerException("Invalid Namespace Prefix");
	        else if ("w".equals(prefix))
	            return "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
	        else    
	            return XMLConstants.NULL_NS_URI;
	    }
	    
	    @Override
	    public String getPrefix(String arg0) {
	        return null;
	    }

	    @Override
	    public Iterator getPrefixes(String arg0) {
	        return null;
	    }

	}
	
	public void prototype(){
		System.out.print("prototype start\n\n");
		
		FileInputStream fisZip;
		ZipInputStream zisOrg;
		
		FileOutputStream fosZip;
		ZipOutputStream zosDest;
		
		ZipEntry iter = null;
		
		StringWriter sw = null;
		try{
			fisZip = new FileInputStream( new File("C:/Users/root/Desktop/1.docx") );
			zisOrg = new ZipInputStream( fisZip );
			
			
			
			fosZip = new FileOutputStream( new File("C:/Users/root/Desktop/1.docx") );
			fosZip.close();
			
			
			
			while((iter = zisOrg.getNextEntry()) != null){
				//System.out.print(iter.getName() + "\n" );
				
				if(iter.getName().equals("word/document.xml") == true){
					
					StringBuffer strBuf = new StringBuffer();
					String str;
					BufferedReader br = new BufferedReader(new InputStreamReader(zisOrg));
					while((str = br.readLine()) != null){
						strBuf.append(str);
					}
					
					
					String strXml = new String(strBuf);
					
					//System.out.println(strXml);
					
					DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
					
					docBuilderFactory.setNamespaceAware(true);
					
					DocumentBuilder parser = docBuilderFactory.newDocumentBuilder();
					
					
					ByteArrayInputStream bis = new ByteArrayInputStream( strXml.getBytes() );
					
					
					Document doc = parser.parse(bis);
					
					//Element eleRoot = doc.getDocumentElement();
					//System.out.println( "root:" + eleRoot.getNodeName() );
					
					
					XPathFactory xPathFactory = XPathFactory.newInstance();
					XPath xpath = xPathFactory.newXPath();
					//NamespaceContext ns = new NamespaceContext("");
					//XMLConstants.XML_NS_PREFIX
					//xpath.setNamespaceContext( );
					xpath.setNamespaceContext(new OOXMLNamespaceContext());
					XPathExpression expr = xpath.compile("//w:t");
					
					NodeList nodes = (NodeList)(Object)expr.evaluate(doc, XPathConstants.NODESET);
					for(int i =0; i < nodes.getLength(); i++){
						System.out.println(nodes.item(i).getNodeName());
						
						nodes.item(i).setTextContent("hello1111111111");
						
					}
					

					
					
					  TransformerFactory factory = TransformerFactory.newInstance();
					  Transformer former = factory.newTransformer();
					  former.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
					  former.setOutputProperty(OutputKeys.INDENT, "yes");
					  
					  sw = new StringWriter();
					  StreamResult result = new StreamResult(sw);
					  DOMSource source = new DOMSource(doc);
					  former.transform(source, result);
					  System.out.println(sw.toString());
					  String strbbXml = sw.toString();
					  strbbXml = strbbXml.trim();
					  
					  if((strbbXml.startsWith("<@@") == true) && (strbbXml.endsWith("@@>") == true)){
						  
					  }
					  
					  
					  System.out.println(iter.getName());
				}
			}
			
			
			
			
			
			
		}catch(Exception e){
			
			System.out.print("----error\n\n" + e.toString());
		}
		
		
		
		
		
		System.out.print("prototype end\n\n");
	}
	
	/**
	 * 지정한 ID에 해당하는 필드값을 반환하는 함수
	 * @param _strFieldID 필드 ID
	 * 
	 * TODO : server 요청하고 받는 루틴 추가
	 * 
	 * @return
	 */
	public String getFieldValue(String _strFieldID){
		String strResult = "채워진값";
		return strResult;
	}
	
	
	/**
	 * TextRun에 포함된 text를 반환한다.
	 * @param _nodeTextRun <w:r> 노드
	 * @return TextRun에 포함된 text
	 */
	public String getTextFromTextRun(Node _nodeTextRun){
		NodeList nodelistChild = _nodeTextRun.getChildNodes();
		
		for(int iter = 0; iter < nodelistChild.getLength(); iter++){
			Node nodeCur = nodelistChild.item(iter);
			
			if(nodeCur.getNodeName().equals("w:t") == true){
				return nodeCur.getTextContent();
			}
		}
		
		return null;
	}
	
	/**
	 * Paragraph에 포함된 text를 반환한다
	 * @param _nodeParagraph <w:p> 노드
	 * @return Paragraph에 포함된 text
	 */
	public String getTextFromParagraph(Node _nodeParagraph){
		
		NodeList nodelistChild = _nodeParagraph.getChildNodes();
		
		String strTextPara = "";
		
		for(int iter = 0; iter < nodelistChild.getLength(); iter++){
			Node nodeCur = nodelistChild.item(iter);
			
			if(nodeCur.getNodeName().equals("w:r") == true){
				String strTextRun = getTextFromTextRun(nodeCur);
				if(strTextRun != null){
					strTextPara += strTextRun;
				}
			}
		}
		
		return strTextPara;
	}
	
	/**
	 * TextRun에 지정된 Text를 set한다
	 * @param _nodeTextRun TextRun Node
	 * @param str 문자열
	 * @return
	 */
	public int setTextToTextRun(Node _nodeTextRun, String str){
		NodeList nodelistChild = _nodeTextRun.getChildNodes();
		
		for(int iter = 0; iter < nodelistChild.getLength(); iter++){
			Node nodeCur = nodelistChild.item(iter);
			
			if(nodeCur.getNodeName().equals("w:t") == true){
				nodeCur.setTextContent(str);
			}
		}
		
		return STATE_OK;
	}
	
	
	/**
	 * 지정한 Paragraph노드에 지정한 문자열을 OOXML로 표현하여 넣는다. 이때 글꼴 스타일 기준은 첫번째 TextRun이다.
	 * @param _nodeParagraph 지정 w:p
	 * @param _str p에 들어갈 문자열
	 * @return 성공 STATE_OK, 실패 : 에러코드
	 */
	public int setTextToParagraph(Node _nodeParagraph, String _str){
		NodeList nodelistChild = _nodeParagraph.getChildNodes();
		
		Node nodeFristTextRun = null;
		Node nodeMainNode = null;
		
		ArrayList<Node> listnodeTextRun = new ArrayList<Node>();
		
		for(int iter = 0; iter < nodelistChild.getLength(); iter++){
			Node nodeCur = nodelistChild.item(iter);
			
			if(nodeCur.getNodeName().equals("w:r") == true){
				nodeFristTextRun = nodeCur;
				nodeMainNode = nodeFristTextRun.cloneNode(true);
				
				listnodeTextRun.add(nodeCur);
			}
		}
		
		
		//기존 textRun삭제
		for(int iter = 0; iter < listnodeTextRun.size() ; iter++){
			_nodeParagraph.removeChild(listnodeTextRun.get(iter));
		}
		
		setTextToTextRun(nodeMainNode, _str);
		_nodeParagraph.appendChild(nodeMainNode);
		
		return STATE_OK;
	}
	
	
	/**
	 * 입력으로 들어온 string을 파싱하여 필드부분을 채워준 후 xml을 string으로 리턴하는 함수
	 * @param _xmlString 입력 XML String
	 * @param _retString 출력 XML String
	 * @return 성공 STATE_OK, 실패 : 에러코드
	 */
	public int settingDocumentXML(String _xmlString, StringBuilder _retString){
		
		try{
			DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
			docBuilderFactory.setNamespaceAware(true);
			DocumentBuilder parser = docBuilderFactory.newDocumentBuilder();
			
			ByteArrayInputStream bis = new ByteArrayInputStream( _xmlString.getBytes() );
			
			Document doc = parser.parse(bis);
			
			XPathFactory xPathFactory = XPathFactory.newInstance();
			XPath xpath = xPathFactory.newXPath();
			
			xpath.setNamespaceContext(new OOXMLNamespaceContext());
			XPathExpression expr = xpath.compile("//w:p");
			
			NodeList nodes = (NodeList)expr.evaluate(doc, XPathConstants.NODESET);
			for(int i =0; i < nodes.getLength(); i++){
				Node nodeCur = nodes.item(i);
				
				String strText = getTextFromParagraph(nodeCur);
				strText = strText.trim();
				
				if ((strText.startsWith("<@@") == true) && (strText.endsWith("@@>") == true)){
					String strId = strText.substring(3, strText.length() - 3);
					String strValue = getFieldValue(strId);
					
					if(strValue != null){
						setTextToParagraph(nodeCur, strValue);
					}
				}
				
			}

			TransformerFactory factory = TransformerFactory.newInstance();
			Transformer former = factory.newTransformer();
			former.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
			former.setOutputProperty(OutputKeys.INDENT, "no");
			  
			StringWriter sw = new StringWriter();
			StreamResult result = new StreamResult(sw);
			DOMSource source = new DOMSource(doc);
			former.transform(source, result);
			
			_retString.append( sw.toString() );
			
			
		}catch(Exception e){
			System.out.println(e.toString());
			return STATE_ERROR_XML_PARSING_FAIL;
		}
		
		return STATE_OK;
	}
	
	
	
	
	/**
	 * 문서와 database url, 프로젝트ID를 받아 필드에 정보를 채워 지정한 경로에 문서를 생생해 주는 함수
	 * 
	 * 동작방식:
	 * DOCX는 OOXML을 zip형식으로 묶은 형태임, 우리는 document.xml을 만 처리하면 되기 때문에
	 * 나머지 파일은 iter를 돌면서 그대로 out하고 document.xml을 만났을때만 dom을 이용해서 편경 후 저장하도록 함   
	 * 
	 * @param _strDocumentPath 문서 원본(양식)
	 * @param _strDestDocumentPath 생성될 문서 경로
	 * @param _strProjectDatabaseURL databate 서버 주소
	 * @param _strProjectId 프로젝트 ID
	 * @return 성공 STATE_OK, 실패 : 에러코드
	 */
	public int createrDocument(String _strDocumentPath, String _strDestDocumentPath, String _strProjectDatabaseURL, String _strProjectId){
		
		File fileOrg = new File(_strDocumentPath);
		File fileDest = new File(_strDestDocumentPath);
		
		if(fileOrg.exists() == false){
			return STATE_ERROR_INPUTFILE_NOT_EXIST;
		}

		
		try{
			FileInputStream fisZip =  new FileInputStream(fileOrg);
			ZipInputStream zisOrg = new ZipInputStream( fisZip );
			
			
			FileOutputStream fosZip = new FileOutputStream(fileDest);
			ZipOutputStream zosDest = new ZipOutputStream(fosZip);
		
			ZipEntry inputIter = null;
			ZipEntry outIter = null;
			
			while((inputIter = zisOrg.getNextEntry()) != null){
				//System.out.print(iter.getName() + "\n" );
				
				String strPathInZip = inputIter.getName();
				outIter = new ZipEntry(strPathInZip);
				
				zosDest.putNextEntry(outIter);
				
				if(inputIter.getName().equals("word/document.xml") == true){
					//word/document.xml을 만났을때 파싱 후 정보를 채워 처리한다.
					
					StringBuffer strBuf = new StringBuffer();
					String str = null;
					StringBuilder strResult = new StringBuilder();
					
					BufferedReader br = new BufferedReader(new InputStreamReader(zisOrg));
					while((str = br.readLine()) != null){
						strBuf.append(str);
					}
					
					settingDocumentXML(strBuf.toString(), strResult);
					
					zosDest.write(strResult.toString().getBytes());
					
				}else{
					//word/document.xml나머지 파일은 그대로 복사한다.
					byte buf[] = new byte[1024];
					
					while(true){
						//나머지 파일은 그냥 복사한다
						int nCnt = zisOrg.read(buf);
						
						if(nCnt <= 0){
							break;
						}
						
						zosDest.write(buf, 0, nCnt);
					}
					
					zosDest.closeEntry();
				}
			}
			
			zisOrg.close();
			zosDest.close();
			
			fisZip.close();
			fosZip.close();
			
		}catch(Exception e){
			System.out.println(e.toString());
			
			return STATE_ERROR;
		}
		
		return STATE_OK;
	}
	
	
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		documentCreator temp = new documentCreator();
		temp.createrDocument("C:/Users/root/Desktop/1.docx", "C:/Users/root/Desktop/12.docx", "", "");
	}

}
